package com.jyt.peripheral.weight

import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Handler
import android.os.Looper
import android.serialport.SerialPort
import android.serialport.SerialPortFinder
import android.widget.Toast
import com.jyt.peripheral.runOnUiThread
import com.jyt.peripheral.toast
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.lang.Exception
import java.security.InvalidParameterException
import java.util.concurrent.TimeUnit

/**
 * @author 陈聪
 * date 2021/5/6:10:15
 * 电子秤
 * 用于初始化连接和添加重量监听
 */
open class WeightManager {

    companion object {
        var mSerialPortFinder: SerialPortFinder? = SerialPortFinder()
        private var mSerialPort: SerialPort? = null
        private var instance: WeightManager? = null

        /**电子秤端口变动回调*/
        var onConfigChange: (() -> Unit)? = null
        var path: String? = null
        var baudrate: Int? = null
        private var mContext: Application? = null

        fun getInstance(context: Application? = null): WeightManager {
            synchronized(WeightManager) {
                if (instance == null) {
                    if(context!=null){
                        mContext = context
                    }
                    instance = WeightManager()
                }
                return instance!!
            }
        }

        protected var mOutputStream: OutputStream? = null
        private var mInputStream: InputStream? = null
        private var mReadThread: ReadThread? = null

        /**
         * 电子秤监听
         */
        private var weightListeners: ArrayList<((Boolean, String) -> (Unit))>? = null
        private fun onDataReceived(buffer: ByteArray?, size: Int) {
            mContext?.runOnUiThread {
                try {
                    val result = String(buffer!!, 0, size)
                    when {
                        result.contains("F") -> {
                            // '重量溢出或没有开机归零';
                            weightListeners?.forEach {
                                it.invoke(false, "重量溢出或没有开机归零")
                            }
                        }
                        result.contains("S") || result.contains("U") -> {
                            val weight = result.substring(3, 12.coerceAtMost(result.length))
                            // S'重量稳定';// U'重量不稳定';
                            weightListeners?.forEach {
                                it.invoke(true, weight)
                            }
                        }
                        else -> {
                            weightListeners?.forEach {
                                it.invoke(false, "电子秤配置异常")
                            }
                        }
                    }
                    getInstance().isConnected = true
                } catch (e: Exception) {
                    weightListeners?.forEach {
                        it.invoke(false, "电子秤异常")
                    }
                    getInstance().isConnected = false
                }
            }
        }
    }

    var isConnected = false
        get() {
            if (!field) {
                toNotifyMsg("电子秤未连接")
            }
            return field
        }

    init {
        onConfigChange = {
            getPathAndRate()
            reConnect()
        }
    }

    /**
     * 添加电子秤监听
     */
    fun toAddWeightListener(l: ((Boolean, String) -> (Unit))?) {
        if (weightListeners == null) {
            weightListeners = ArrayList()
        }
        if (l != null && weightListeners?.contains(l) == false) {
            weightListeners?.add(l)
        }
        if (!isConnected) {
            toNotifyMsg("电子秤未连接")
        }
    }

    /**
     * 添加电子秤监听
     */
    fun toRemoveWeightListener(l: ((Boolean, String) -> (Unit))?) {
        if (weightListeners == null) {
            return
        }
        if (l != null && weightListeners!!.contains(l)) {
            weightListeners?.remove(l)
        }
    }

    /**
     * 进行电子秤连接
     */
    fun toConnectWeight() {
        mContext?.toast("连接电子秤")
        if (mReadThread == null) {
            mContext?.toast("正在连接电子秤")
            try {
                mSerialPort = getSerialPort()
                mOutputStream = mSerialPort!!.outputStream
                mInputStream = mSerialPort!!.inputStream
                mReadThread = ReadThread()
                mReadThread?.start()
                mContext?.toast("端口连接成功")
            } catch (e: SecurityException) {
                toNotifyMsg("端口无读写权限，请重新配置")
            } catch (e: IOException) {
                toNotifyMsg("端口错误，请重新配置")
            } catch (e: InvalidParameterException) {
                toNotifyMsg("端口错误，请重新配置")
            } catch (e: java.io.FileNotFoundException) {
                toNotifyMsg("连接电子秤失败")
            }
        }
    }

    private fun toNotifyMsg(str: String) {
        mContext?.toast(str)
        weightListeners?.forEach {
            mContext?.runOnUiThread {
                it.invoke(false, str)
            }
        }
    }

    /**
     * 进行电子秤设置
     */
    fun goWeightSetting() {
        mContext?.startActivity(
            Intent(
                mContext!!, SerialPortPreferences::class.java
            )
        )
    }

    /**
     * 获取串口
     */
    @Throws(SecurityException::class, IOException::class, InvalidParameterException::class)
    fun getSerialPort(): SerialPort? {
        if (mSerialPort == null) {
            /* Read serial port parameters */
            getPathAndRate()

            if (path!!.isEmpty() || baudrate == -1) {
                throw InvalidParameterException()
            }

            /**波特率9600 Baud
            数据位:8 data bits
            奇偶检验:None parity
            停止位:1 stop bit  */
            /* Open the serial port */
            //mSerialPort = new SerialPort(new File(path), baudrate, 0);
            val serialPort = SerialPort //
                .newBuilder(path, baudrate!!) // 串口地址地址，波特率
                .parity(0) // 校验位；0:无校验位(NONE，默认)；1:奇校验位(ODD);2:偶校验位(EVEN)
                .dataBits(8) // 数据位,默认8；可选值为5~8
                .stopBits(1) // 停止位，默认1；1:1位停止位；2:2位停止位
                .build()
            mSerialPort = serialPort
        }
        return mSerialPort
    }

    private fun getPathAndRate() {
        val packageName: String = mContext?.packageName!!
        val sp: SharedPreferences = mContext?.getSharedPreferences(
            packageName + "_preferences", Context.MODE_PRIVATE
        )!!
        path = sp.getString("DEVICE", "/dev/ttyUSB0")
//        path = sp.getString("DEVICE", "/dev/ttyS3")
        baudrate = Integer.decode(sp.getString("BAUDRATE", "9600"))
    }

    private class ReadThread : Thread() {
        override fun run() {
            super.run()
            while (!isInterrupted) {
                var size: Int
                try {
                    val buffer = ByteArray(512)
                    if (mInputStream == null) return
                    size = mInputStream!!.read(buffer)
                    if (size > 0) {
                        onDataReceived(buffer, size)
                    }
                    try {
                        sleep(50)
                    } catch (e: InterruptedException) {
                        e.printStackTrace()
                    }
                } catch (e: IOException) {
                    e.printStackTrace()
                    return
                } finally {
                    if (mContext == null) {
                        getInstance().closeSerialPort()
                    }
                }
            }
        }
    }

    /**
     * 关闭串口
     */
    @SuppressLint("CheckResult")
    fun reConnect() {
        isConnected = false
        if (mReadThread != null) {
            mReadThread?.interrupt()
            mReadThread = null
        }
        if (mSerialPort != null) {
            mSerialPort!!.close()
            mSerialPort = null
        }
        mInputStream?.close()
        mInputStream = null
        mOutputStream?.close()
        mOutputStream = null
        Observable.timer(300, TimeUnit.MILLISECONDS)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe {
                toConnectWeight()
            }

    }

    /**
     * 关闭串口
     */
    fun closeSerialPort() {
        isConnected = false
        if (mSerialPort != null) {
            mSerialPort!!.close()
            mSerialPort = null
        }
        if (weightListeners != null) {
            weightListeners!!.clear()
            mSerialPort = null
        }
        mInputStream?.close()
        mInputStream = null
        mOutputStream?.close()
        mOutputStream = null
        mSerialPort = null
        mSerialPortFinder = null
    }

//    数据格式:
//    status,sign,Weight,AUnit,flag
//
//    status说明:
//    status = 'F'   // '重量溢出或没有开机归零';
//    status = 'S'   // '重量稳定';
//    fstatus= 'U'   // '重量不稳定';
//
//    完整重量显示说明:
//    sign+Weight+AUnit
//
//    flag说明:
//    flag = 16; //已归零
//    flag = 30; //已去皮
//
//    4.去皮
//    procedure __udeTare; stdcall;external 'SensorDll.dll';
//
//    5.归零
//    procedure __uCleart;stdcall;external 'SensorDll.dll';
//
//    6.设置计量单位
//    function __SetUnitValue(Value:Word;Id:PChar):Boolean;stdcall;external 'SensorDll.dll';
//    说明:
//    Id = '123456'
//    Value =0 公斤
//    =1 克
//    =2 港斤
//    =3 港两
//    =4 台斤
//    =5 台两
//    =6 磅
//    =8 盎司

}

